package maquinaV;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Stack;
import java.util.logging.Level;
import java.util.logging.Logger;
import GUI.Interfaz;
import java.text.DecimalFormat;

import javax.swing.JOptionPane;


public class MaquinaP {
    private String out="";
    private String ultimo;
    int CProg;
    MemoriaDatos MemDat;
    Stack<String> Pila = new Stack<String>();
    MemoriaInstrucciones Prog;
    boolean parada;
    boolean modoTraza;
    private String errores="";
    boolean hayErrores=false;
    
    public MaquinaP(ArrayList al,boolean modo){
    	CProg=0;
    	Prog=new MemoriaInstrucciones();
    	MemDat=new MemoriaDatos();
    	Pila= new Stack<String>();
    	modoTraza=modo;
    	parada=false;
    	MemDat.cargarDatos(al);
    	Prog.cargaCod("codigop.txt");
    }
    public void ejecuta(){

        String s="";
	if(modoTraza==true){
            Instruccion inst=Prog.dameInstruccion(CProg);
            String Nombre=inst.getNombre();
            String Valor=inst.getValor();
            String Tipo=inst.getTipo();
            ejecutaInstruccion(Nombre,Valor,Tipo);
        }
        else {
        while(parada==false){
            Instruccion inst=Prog.dameInstruccion(CProg);
            String Nombre=inst.getNombre();
            String Valor=inst.getValor();
            String Tipo=inst.getTipo();
            ejecutaInstruccion(Nombre,Valor,Tipo);
   	}
        }
   }
   
   
   public ArrayList<String> muestraEstadoPila(){
	   ArrayList<String> estadoPila=new ArrayList<String>();
       System.out.println("Estado de la pila antes de la ejecucion de la instruccion:");
       if(Pila.size()>0){
           estadoPila.add(("Cima: "+Pila.elementAt(Pila.size()-1)).toString());
           for(int i=Pila.size()-2;i>=0;i--){
               estadoPila.add("     "+(Pila.elementAt(i)).toString());
           }
       }else{
           System.out.println("Pila vacia");
       }
      return estadoPila;
   }

   public ArrayList<String> muestraEstadoMemoria(){

       ArrayList<TipoMemoria> variables=MemDat.dameVariables();
       ArrayList<String> estadoMemoria=new ArrayList<String>();
       for(int i=0;i<variables.size();i++){
        estadoMemoria.add(variables.get(i).toString());
       }
       return estadoMemoria;
    }
   
     
   private void ejecutaInstruccion(String Nombre,String Valor,String Tipo){
	   
	   if(Nombre.equals("apila_dir")){
		   apila_dir(Valor);
	   }
	   if(Nombre.equals("desapila_dir")){
		   desapila_dir(Valor);
	   }
	   if(Nombre.equals("apila")){
		   apila(Valor,Tipo);
	   }
	   if(Nombre.equals("ADD")||Nombre.equals("SUB")||Nombre.equals("MUL")||Nombre.equals("DIV")||Nombre.equals("MOD")){
		   OpAritmetica(Nombre);
	   }
	   if(Nombre.equals("in")||Nombre.equals("out")){
		   OpInOut(Nombre,Valor);
	   }
	   if(Nombre.equals("CASTING_E")||Nombre.equals("CASTING_R")){
		   OpCasting(Nombre);
	   }
	   if(Nombre.equals("ME")||Nombre.equals("MA")||Nombre.equals("MEI")||Nombre.equals("MAI")||Nombre.equals("IG")|Nombre.equals("NIG")){
		   OpComp(Nombre);
	   }
	   if(Nombre.equals("OR")||Nombre.equals("AND")||Nombre.equals("NOT")){
		    OpLog(Nombre);
	   }
       if(Nombre.equals("CS")){
	       OpCambioSigno();
       }
	   if(Nombre.equals("STOP")){
	    	parada=true;	    	
	   }
       if(Nombre.equals("duplicar")){
        duplica();
       }
	   CProg++; 
   }
   

   public void apila(String Valor,String Tipo){
	   Pila.push(Valor+" "+Tipo);
       ultimo=Valor;
  }

   public void duplica(){       
       Pila.push(ultimo);
   }
   public void apila_dir(String dir){
	   TipoMemoria dato=MemDat.dameValor(Integer.valueOf(dir));
	   Pila.push(dato.getValor());
       ultimo=dato.getValor();
   }

   public void desapila_dir(String dir){
	   TipoMemoria t=MemDat.dameValor(Integer.valueOf(dir));
	   String val=Pila.pop();
       t.setValor(val);
	   MemDat.setValor(dir,t);
       ultimo=val;

  }

   public void OpAritmetica(String Nombre){
	      
	      String val2=Pila.pop();
	      String val1=Pila.pop();
	      String[] sval1=dameNumeroYTipo(val1);
	      String[] sval2=dameNumeroYTipo(val2);
	      
	      if (Nombre.equals("ADD")){
	          if(sval1[1].equals("0")&&sval2[1].equals("0")){
	        	  Integer i= Integer.valueOf(sval1[0])+Integer.valueOf(sval2[0]);
	        	  Pila.push(String.valueOf(i)+" 0");
                  ultimo=String.valueOf(i);
	          }
	          if(sval1[1].equals("0")&&sval2[1].equals("1")){
	        	  Double i= Double.valueOf(sval1[0])+Double.valueOf(sval2[0]);
	        	  Pila.push(String.valueOf(i)+" 1");
                  ultimo=String.valueOf(i);
	          }
	          if(sval1[1].equals("1")&&sval2[1].equals("0")){
	        	  Double i= Double.valueOf(sval1[0])+Double.valueOf(sval2[0]);
	        	  Pila.push(String.valueOf(i)+" 1");
                  ultimo=String.valueOf(i);
	          }
	         /* if(sval1[1]=="2"&&sval2[1]=="0"){
	        	  Integer i= Integer.valueOf(sval1[0])+Integer.valueOf(sval2[0]);
	        	  Pila.push(String.valueOf(i));
	          }
	          if(sval1[1]=="0"&&sval2[1]=="2"){
	        	  Integer i= Integer.valueOf(sval1[0])+Integer.valueOf(sval2[0]);
	        	  Pila.push(String.valueOf(i));
	          }*/
	          if(sval1[1].equals("1")&&sval2[1].equals("1")){
	        	  Double i= Double.valueOf(sval1[0])+Double.valueOf(sval2[0]);
	        	  Pila.push(String.valueOf(i)+" 1");
                  ultimo=String.valueOf(i);
	          }
	      }
	      if (Nombre.equals("SUB")){
	          if(sval1[1].equals("0")&&sval2[1].equals("0")){
	        	  Integer i= Integer.valueOf(sval1[0])-Integer.valueOf(sval2[0]);
	        	  Pila.push(String.valueOf(i)+" 0");
                  ultimo=String.valueOf(i);
	          }
	          if(sval1[1].equals("0")&&sval2[1].equals("1")){
	        	  Double i= Double.valueOf(sval1[0])-Double.valueOf(sval2[0]);
	        	  Pila.push(String.valueOf(i)+" 1");
                  ultimo=String.valueOf(i);
	          }
	          if(sval1[1].equals("1")&&sval2[1].equals("0")){
	        	  Double i= Double.valueOf(sval1[0])-Double.valueOf(sval2[0]);
	        	  Pila.push(String.valueOf(i)+" 1");
                  ultimo=String.valueOf(i);
	          }
	         /* if(sval1[1]=="2"&&sval2[1]=="0"){
	        	  Integer i= Integer.valueOf(sval1[0])-Integer.valueOf(sval2[0]);
	        	  Pila.push(String.valueOf(i));
	          }
	          if(sval1[1]=="0"&&sval2[1]=="2"){
	        	  Integer i= Integer.valueOf(sval1[0])-Integer.valueOf(sval2[0]);
	        	  Pila.push(String.valueOf(i));
	          }*/
	          if(sval1[1].equals("1")&&sval2[1].equals("1")){
	        	  Double i= Double.valueOf(sval1[0])-Double.valueOf(sval2[0]);
	        	  Pila.push(String.valueOf(i)+" 1");
                  ultimo=String.valueOf(i);
	          }
	      }
	       if (Nombre.equals("MUL")){
	          if(sval1[1].equals("0")&&sval2[1].equals("0")){
	        	  Integer i= Integer.valueOf(sval1[0])*Integer.valueOf(sval2[0]);
	        	  Pila.push(String.valueOf(i)+" 0");
                  ultimo=String.valueOf(i);
	          }
	          if(sval1[1].equals("0")&&sval2[1].equals("1")){
	        	  Double i= Double.valueOf(sval1[0])*Double.valueOf(sval2[0]);
	        	  DecimalFormat p=new DecimalFormat("###########.##");
	        	  String s=p.format(i);
	        	  String aux=s.replace(",",".");
                  Pila.push(aux+" 1");
                  ultimo=String.valueOf(i);
	          }
	          if(sval1[1].equals("1")&&sval2[1].equals("0")){
	        	  Double i= Double.valueOf(sval1[0])*Double.valueOf(sval2[0]);
	        	  DecimalFormat p=new DecimalFormat("###########.##");
	        	  String s=p.format(i);
	        	  String aux=s.replace(",",".");
                  Pila.push(aux+" 1");
                  ultimo=String.valueOf(i);
	          }
	         /* if(sval1[1]=="2"&&sval2[1]=="0"){
	        	  Integer i= Integer.valueOf(sval1[0])*Integer.valueOf(sval2[0]);
	        	  Pila.push(String.valueOf(i));
	          }
	          if(sval1[1]=="0"&&sval2[1]=="2"){
	        	  Integer i= Integer.valueOf(sval1[0])*Integer.valueOf(sval2[0]);
	        	  Pila.push(String.valueOf(i));
	          }*/
	          if(sval1[1].equals("1")&&sval2[1].equals("1")){
	        	  Double i= Double.valueOf(sval1[0])*Double.valueOf(sval2[0]);
	        	  DecimalFormat p=new DecimalFormat("###########.##");
                          String s=p.format(i);
                          String aux=s.replace(",",".");
                          Pila.push(aux+" 1");
                          ultimo=String.valueOf(aux);
	          }
	      }

	      if (Nombre.equals("DIV")){
	    	  try{
	          if(sval1[1].equals("0")&&sval2[1].equals("0")){
	        	  int i= ((int)Integer.valueOf(sval1[0])/Integer.valueOf(sval2[0]));
	        	  Pila.push(String.valueOf(i)+" 0");
                  ultimo=String.valueOf(i);
	          }
	          if(sval1[1].equals("0")&&sval2[1].equals("1")){
	        	  Double i= Double.valueOf(sval1[0])/Double.valueOf(sval2[0]);


	        	  if(!Double.isInfinite(i)){
	        		  DecimalFormat p=new DecimalFormat("###########.##");
		        	  String s=p.format(i);
		        	  String aux=s.replace(",",".");
                                  Pila.push(aux+" 1");
                                  ultimo=String.valueOf(aux);
	        	  }
	        	  else{
	        		  errores=errores+"Error: division por cero, linea: "+String.valueOf(CProg+1)+"\n";
	        		  parada=true;
                      hayErrores=true;
	        	  }
	          }
	          if(sval1[1].equals("1")&&sval2[1].equals("0")){
	        	  Double i= Double.valueOf(sval1[0])/Double.valueOf(sval2[0]);	       	  //Pila.push(String.valueOf(i)+" 1");
	        	  if(!Double.isInfinite(i)&&!Double.isNaN(i)){
	        		  DecimalFormat p=new DecimalFormat("###########.##");
		        	  String s=p.format(i);
		        	  String aux=s.replace(",",".");
                                  Pila.push(aux+" 1");
                                  ultimo=String.valueOf(aux);
	        	  }
	        	  else{
	        		  errores=errores+"Error: division por cero, linea: "+String.valueOf(CProg+1)+"\n";
	        		  parada=true;
                      hayErrores=true;
	        	  }

	          }
	         /* if(sval1[1]=="2"&&sval2[1]=="0"){
	        	  Integer i= Integer.valueOf(sval1[0])/Integer.valueOf(sval2[0]);
	        	  Pila.push(String.valueOf(i));
	          }
	          if(sval1[1]=="0"&&sval2[1]=="2"){
	        	  Integer i= Integer.valueOf(sval1[0])/Integer.valueOf(sval2[0]);
	        	  Pila.push(String.valueOf(i));
	          }*/
	          if(sval1[1].equals("1")&&sval2[1].equals("1")){
	        	  Double i= Double.valueOf(sval1[0])/Double.valueOf(sval2[0]);	       	  //Pila.push(String.valueOf(i)+" 1");
	        	  if(!Double.isInfinite(i)&&!Double.isNaN(i)){
	        		  DecimalFormat p=new DecimalFormat("###########.##");
		        	  String s=p.format(i);
                                 String aux=s.replace(",",".");
                                 Pila.push(aux+" 1");
                                 ultimo=String.valueOf(aux);
	        	  }
	        	  else{
	        		  errores=errores+"Error: division por cero, linea: "+String.valueOf(CProg+1)+"\n";
	        		  parada=true;
                      hayErrores=true;
	        	  }

	          }
		      }
		      catch(ArithmeticException e){
		        	errores=errores+"Error: division por cero, linea: "+String.valueOf(CProg+1)+"\n";
		        	parada=true;
                    hayErrores=true;
		      }
		  }

	      if (Nombre.equals("MOD")){
	    	  try{
	    		  if(sval1[1].equals("0")&&sval2[1].equals("0")){
	 	        	  Integer i= Integer.valueOf(sval1[0])%Integer.valueOf(sval2[0]);
	 	        	  Pila.push(String.valueOf(i)+" 0");
                      ultimo=String.valueOf(i);
	 	          }
	 	          else {
                                errores=errores+"Error: Los operandos del modulo no son de tipo entero, linea: "+String.valueOf(CProg+1)+"\n";
	 	            	parada=true;
                        hayErrores=true;
	 	         }
	 	     }
	 	     catch (ArithmeticException e){
                         errores=errores+"Error: Segundo operando de la operacion modulo es 0, linea: "+String.valueOf(CProg+1)+"\n";
	 	         parada=true;
                 hayErrores=true;
	 	     }
	 	 }
   }
   
   public void OpCasting(String nombre){
	   String val1=Pila.pop();	      
	   String[] sval1=dameNumeroYTipo(val1);
	   if(nombre.equals("CASTING_E")){
		  if (sval1[1].equals("0")){ 
		      Pila.push(sval1[0]+" "+sval1[1]);
              ultimo=sval1[0];
		  }
		  else if (sval1[1].equals("1")){
			  Pila.push(String.valueOf((int)(Double.parseDouble(sval1[0])))+" 0");
              ultimo=String.valueOf((int)(Double.parseDouble(sval1[0])));
		  }
	   }
	   if(nombre.equals("CASTING_R")){
		  if (sval1[1].equals("1")){
			  Pila.push(sval1[0]+" "+sval1[1]);
              ultimo=sval1[0];
		  }
		  else if (sval1[1].equals("0")){
			  Pila.push(String.valueOf((double)(Integer.parseInt(sval1[0])))+" 1");
              ultimo=String.valueOf((double)(Integer.parseInt(sval1[0])));
		  }
	   }	   
   }
   
   public void OpInOut(String Nombre,String Tipo){
	   if (Nombre.equals("in")){
	    boolean valorCorrecto=false;
    	 String textoEnt= "Introduzca entero";
    	 String textoReal="Introduzca real";
	     while(!valorCorrecto){
	     if (Tipo.equals("REAL")){
	    	try{
	    	String var = JOptionPane.showInputDialog(textoReal, "");
	    	Double num= Double.parseDouble(var);
	    	valorCorrecto=true;
	    	Pila.push(var+" 1");
            ultimo=var;
	    	}
	    	catch(Exception e){
	    		textoReal="No era un real";
                hayErrores=true;
	    	}

	     }

	     else if (Tipo.equals("ENT")){
	    	 try{
	    		 String var = JOptionPane.showInputDialog(textoEnt, "");
	    		 int num = Integer.parseInt(var);
	    		 valorCorrecto=true;
	    		 Pila.push(var+" 0");
                 ultimo=var;
	    	 }
	    	 catch(Exception e)
	    	 {
	    		 textoEnt="No era un entero";
                 hayErrores=true;
	    	 }
	     }
	     }
	  }
	  else if (Nombre.equals("out")){
		  String[] s=dameNumeroYTipo(Pila.pop());
          ultimo=s[0];
		  out=out+s[0]+"\n";
      }
	   		
   }
   
   public void OpComp(String Nombre){
	   String val1=Pila.pop();	      
	   String[] sval1=dameNumeroYTipo(val1);
	   String val2=Pila.pop();	      
	   String[] sval2=dameNumeroYTipo(val1);
	   	   
	   if(sval1[1].equals("0")&&sval2[1].equals("0")){
     	  if(Nombre.equals("ME")){
		  if(Integer.valueOf(sval1[1])<Integer.valueOf(sval2[1])){
			   Pila.push("1");
		   }
		   else Pila.push("0");
     	  }
	   if(Nombre.equals("MA")){
		   if(Integer.valueOf(sval1[1])>Integer.valueOf(sval2[1])){
			   Pila.push("1");
		   }
		   else Pila.push("0");
	   }
	   if(Nombre.equals("MEI")){
		   if(Integer.valueOf(sval1[1])<=Integer.valueOf(sval2[1])){
			   Pila.push("1");
		   }
		   else Pila.push("0");
	   }
	   if(Nombre.equals("MAI")){
		   if(Integer.valueOf(sval1[1])>=Integer.valueOf(sval2[1])){
			   Pila.push("1");
		   }
		   else Pila.push("0");
	   }
	   }
	   
	   else{
	     	  if(Nombre.equals("ME")){
			  if(Double.valueOf(sval1[1])<Double.valueOf(sval2[1])){
				   Pila.push("1");
			   }
			   else Pila.push("0");
	     	  }
		   if(Nombre.equals("MA")){
			   if(Double.valueOf(sval1[1])>Double.valueOf(sval2[1])){
				   Pila.push("1");
			   }
			   else Pila.push("0");
		   }
		   if(Nombre.equals("MEI")){
			   if(Double.valueOf(sval1[1])<=Double.valueOf(sval2[1])){
				   Pila.push("1");
			   }
			   else Pila.push("0");
		   }
		   if(Nombre.equals("MAI")){
			   if(Double.valueOf(sval1[1])>=Double.valueOf(sval2[1])){
				   Pila.push("1");
			   }
			   else Pila.push("0");
		   }
		   }
   }
   
   public void OpLog(String Nombre){

	      String val1=Pila.pop();
	      String[] sval1=dameNumeroYTipo(val1);
	
	      
	      if (Nombre.equals("OR")){
	    	  String val2=Pila.pop();
	    	  String[] sval2=dameNumeroYTipo(val2);
	    	 if(sval1[1].equals("0")&&sval2[1].equals("0")){
	        	  if (Integer.valueOf(val1)==0 && Integer.valueOf(val1)==0){
	        		  Pila.push("0");
	        	  }
	        	  else Pila.push("1");
	          }
          }

	      if (Nombre.equals("AND")){
	    	  String val2=Pila.pop();
	    	  String[] sval2=dameNumeroYTipo(val2);
	          if(sval1[1]=="0"&&sval2[1]=="0"){
	        	  if (Integer.valueOf(val1)==1 && Integer.valueOf(val1)==1)
	          	  Pila.push("1");	        	  
	          }
	     }

	      if (Nombre.equals("NOT")){
	          if(sval1[1]=="0"){
	        	 if(Integer.valueOf(val1)==1){ 
	        	  	  Pila.push("0");	        	  
	             }
	        	 else if (Integer.valueOf(val1)==0){
	        		 Pila.push("1");
	        	 }
	          }
	     }     
	      
	   
   }
  
   public void OpCambioSigno(){
	   String val1=Pila.pop();
	   String[] sval1=dameNumeroYTipo(val1);
	   if(sval1[1].equals("0")){
     	          
          Pila.push(String.valueOf(- Integer.valueOf(sval1[0])));
       }
	   else if(sval1[1].equals("1")){
		  Pila.push(String.valueOf(-Double.valueOf(sval1[0])));
	   }
   }
   
   public String[] dameNumeroYTipo(String s){
       String []a;
       a=new String[2];
       a[0]="";a[1]="";
       a=s.split("\\s+");
       return a;
   }

   public MemoriaInstrucciones dameInstrucciones(){
       return Prog;
   }

   public String pilaToStrint(){
       String s="";
       String ret="";
       String []aux;
       for(int i=Pila.size();i>=1;i--){
           s=Pila.get(i-1).toString();
           aux=s.split("\\s");
           ret=ret+aux[0]+"\n";
       }
       return ret;
   }
   
   public String memoriaToString(){
       return MemDat.toString();
   }

   public String getOut(){
       return out;
   }
   public String getErrores(){return this.errores;}
   public boolean getHayErrores(){
       return hayErrores;
   }
   public boolean getParada(){
           return parada;
           }
}//fin de clase MaquinaP